#coding:utf-8
import time
import urllib.parse
import json
from bs4 import BeautifulSoup
import requests
import re
import execjs
import ddddocr
import base64
from tool import api
from tool import function
from logger import Logger
from logger import set_log_filename
from pandas import DataFrame
import datetime

today = str(datetime.date.today())
logger = Logger("Kingo_act")
set_log_filename(today)

hidkey_list = ('S20101','S20301','S20403','S40303','S902')
'''
S20101：培养方案
S20301：教学安排（课表）
S20403：考试安排表
S40303：成绩
S902：教评
'''

class ZZIFE(object):
    '''
    教务系统操作
    '''
    HEADERS = {
        'Host': "jw.zzife.edu.cn",
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0",
        'Accept': "image/webp,image/*,*/*;q=0.8",
        'Referer': "https://jw.zzife.edu.cn/cas/login.action",
        'Accept-Encoding': "gzip, deflate, sdch",
        'Accept-Language': "zh-CN,zh;q=0.8"
    }
    headers = {
        'Accept': "text/plain, */*; q=0.01",
        'Accept-Encoding': "gzip, deflate, br, zstd",
        'Accept-Language': "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        'Connection': "keep-alive",
        'Content-Type': "application/x-www-form-urlencoded",
        'Host': "jw.zzife.edu.cn",
        'Origin': "https://jw.zzife.edu.cn",
        'Referer': "https://jw.zzife.edu.cn/cas/logon.action",
        'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0",
        'X-Requested-With': "XMLHttpRequest"
    }

    def __init__(self,userid,password):
        set_log_filename(userid)
        self.userid = str(userid)
        self.password = str(password)
        self.session = requests.Session()
        self.current_path = api.current_path
        self.root_path = api.root_path
        self.data_path = api.data_path

    #登录
    def login(self):
        '''
        登录
        :return: post message
        '''
        #初始化ocr引擎
        ocr = ddddocr.DdddOcr(old=True)
        #初始化js读取
        jsstr = function.enc_js()
        ctx = execjs.compile(jsstr)
        # ocr获取验证码
        image = self.session.get(api.image_api, headers=self.HEADERS, )
        _ssessionid = image.cookies.get("JSESSIONID")
        logger.info(f"\n获取ssessionid:{_ssessionid}")
        png_path = self.data_path / 'img3.png'
        with open(png_path, 'wb') as f:
            f.write(image.content)
        with open(png_path, 'rb') as f:
            image = f.read()
        logger.info(f"获取验证码")
        randnumber = ocr.classification(image)
        logger.info((f"识别验证码:{randnumber}"))
        # 获得必要信息
        response = self.session.get(url=api.enc_url, headers=self.HEADERS)
        enc_text = response.text
        _deskey = re.search(r"var _deskey = '(.*?)';", enc_text).group(1)
        _nowtime = re.search(r"var _nowtime = '(.*?)';", enc_text).group(1)
        logger.info("获取特征信息")
        # 加密
        _params = ctx.call("build_params", self.userid, self.password, randnumber, _ssessionid)
        _params = ctx.call("getEncParams", _params, _deskey, _nowtime)
        data = _params + "&deskey=" + _deskey + "&ssessionid=" + _ssessionid
        logger.info("加密params，并构建data")
        # 发送post请求
        result = self.session.post(api.login_url, headers=self.headers, data=data)
        result = result.json()
        logger.info("发送POST请求")
        referer = api.home_url + result['result']
        self.headers['Referer'] = referer
        return result['message']


    #获取课表
    def timetable(self):
        '''
        获取课表
        '''
        #准备信息
        hidkey = hidkey_list[1]
        xn = str(input('请输入学年（例：2024）'))
        xq = str(input('请输入学期\n'
                       '第一学期输入0，第二学期输入1'))
        xq_real = str(int(xq) + 1)
        xh = self.userid
        kb_path = api.data_path/f"/kb_{xn}-{xq_real}.html"
        logger.info("准备信息")
        #修改请求头
        referer = "student/xkjg.wdkb.jsp?menucode" + hidkey
        self.headers['Referer'] = api.home_url + referer
        self.headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"
        logger.info("修改请求头")
        #编写请求正文
        _params = "xn=" + xn + "&" + "xq=" + xq + "&" + "xh=" + xh
        params = str(base64.b64encode(_params.encode("utf-8")),"utf-8")
        params = "params=" + params
        logger.info("编写请求正文")
        #发送POST请求
        send_url = api.timetable_api + params
        timetable_html = self.session.get(url=send_url, headers=self.headers, timeout=3)
        logger.info("发送POST请求")
        #将结果存储至data目录
        with open(kb_path,'wb+') as f:
            for chunk in timetable_html.iter_content(chunk_size=1024):
                if chunk:
                    f.write(chunk)
            f.close()
        logger.info("将结果存储至data目录")
        function.show_html(kb_path)
        return "课表已存储至data目录,并成功展示"

    #获取培养方案
    def cultivation_plan(self):
        '''
        获取培养方案
        '''
        hidkey = hidkey_list[0]
        #修改请求头
        referer = "student/pyfa.llkc.html?menucode=" + hidkey
        self.headers['Referer'] = api.home_url + referer
        self.headers['Accept'] = "application/json, text/javascript, */*; q=0.01"
        logger.info("修改请求头")
        #获取信息
        my_info = self.session.post(url=api.getmyinfo_api).json()['result']
        my_info = json.loads(my_info)
        yearterm = self.session.post(url=api.yearterm_api,headers = self.headers).json()
        _xq = yearterm["xqName"]
        xn = yearterm["xn"]
        xn1 = str(int(xn) + 1)
        logger.info("获取信息")
        #编写请求正文
        plan_data = f"xn={xn}&xn1={xn1}&xq=0&_xq={_xq}&xh=&nj={my_info['nj']}&fxnj=&menucode={hidkey}&yxdm={my_info['yxdm']}&zydm={my_info['zydm']}&zyfxdm=&fxzydm=&count=1&initQry=0&nj_={my_info['nj']}&fzyxs=zx&menucode_current={hidkey}"
        self.headers['Content-Length'] = str(len(plan_data))
        logger.info("编写请求正文")
        #发送POST请求
        plan = self.session.post(url=api.plan_api,headers = self.headers,data=plan_data)
        logger.info("发送POST请求")
        #提取信息
        soup = BeautifulSoup(plan.text,"html.parser")
        try:
            table = soup.select("table")[-1].select("tr")
        except IndexError:
            return "未获取到培养方案！"
        plan_list = []
        for tr in table[1:]:
            td = tr.select("td")
            try:
                plan_list.append((td[1].text ,td[2].text ,td[3].text ,td[4].text ,td[6].text ,td[7].text))
                # td[1]:学年学期,td[2]:课程,td[3]:学分,td[4]:课程类别,td[6]:,td[7]:总学时
            except:
                pass
        #将信息构建成二维表并存储
        excel_path = api.data_path / f"{self.userid}_plan.xlsx"
        column_list = ["学年学期","课程","学分","课程类别","考核方式","总学时"]
        df = DataFrame(plan_list,columns=column_list)
        df.to_excel(excel_path,index=False)
        logger.info("将信息构建成二维表并存储")
        return f"{self.userid}的培养方案保存至data目录"

    #获取成绩
    def get_grade(self):
        '''
        获取成绩
        '''
        #准备信息
        hidkey = hidkey_list[3]
        xn = str(input("输入查询学年"))
        xn1 = str(int(xn)+1)
        xq = str(input("输入学期：0：上学期，1：下学期"))
        rxnj = "20" + self.userid[6:8]
        print(rxnj)
        logger.info("准备信息")
        #填写请求正文
        data = {
            "sjxz": "sjxz3", # sjxz1：查询入学以来成绩 sjxz2：查询学年成绩 sjxz3：查询学期成绩
            "ysyx": "yscj", #yscj：查询原始成绩 yxcj：查询有效成绩
            "zx": "1", # 主修是否查询 0：不查询 1：查询
            "fx": "1", # 辅修是否查询 0：不查询 1：查询
            "rxnj": f"{rxnj}", # 入学年级
            "btnExport": r"%B5%BC%B3%F6", # “导出”
            "xn": f"{xn}", # 查询学年
            "xn1": f"{xn1}", # 截止学年
            "xq": f"{xq}", # 学期 0：上学期 1：下学期
            "ysyxS": "on",
            "sjxzS": "on",
            "zxC": "on",
            "fxC": "on",
            "xsjd": "1",
            "menucode_current": f"{hidkey}"
        }
        logger.info("填写请求正文")
        #修改请求头
        self.headers['Content-Length'] = str(len(data))
        referer = api.home_url + f"student/xscj.stuckcj.jsp?menucode={hidkey}"
        self.headers['Referer'] = referer
        logger.info("修改请求头")
        #发送POST请求
        grade = self.session.post(url=api.grade_api,headers=self.headers,data=data)
        logger.info("发送POST请求")
        #提取信息
        soup = BeautifulSoup(grade.text, "html.parser")
        try:
            table = soup.select("table")[-1].select("tr")
        except IndexError:
            return "未获取到成绩！"
        grade_list = []
        for tr in table[1:]:
            td = tr.select("td")
            try:
                grade_list.append((td[1].text, td[12].text, td[8].text, td[9].text, td[10].text))
            except IndexError:
                grade_list.append((td[1].text, td[8].text, "NULL", "NULL", "NULL"))
            # td[1]:课程名称,td[12]:综合成绩,td[8]:平时成绩,td[9]:期中成绩,td[10]:期末成绩
        logger.info("提取信息")
        #将信息构建成二维表并存储
        excel_path = api.data_path / f"{xn}-{xn1}-{xq}_grade.xlsx"
        column_list = ["课程名称","综合成绩","平时成绩","期中成绩","期末成绩"]
        df = DataFrame(grade_list,columns=column_list)
        df.to_excel(excel_path,index=False)
        logger.info("将信息构建成二维表并存储")
        return f"{self.userid}的{xn}-{xn1}-{xq}成绩保存至data目录"
    #一键教评
    def jp(self):
        '''
         一键教评
         '''
        #修改请求头
        self.headers['Referer'] = "https://jw.zzife.edu.cn/student/wspj_tjzbpj_wjdcb.jsp?pjzt_m=20&menucode=S902"
        #初始化信息
        pjzt_m = '20'
        menucode = 'S902'
        #获取pjinfo
        pjinfo_data = "pjzt_m=20"
        pjinfo_result = self.session.post(url=api.getPjlcInfo_api,headers = self.headers,data = pjinfo_data)
        pjinfo_text = pjinfo_result.text
        logger.info('获取pjinfo')
        pjinfo_soup = BeautifulSoup(pjinfo_text,'html.parser')
        option_tag = pjinfo_soup.find('option')
        value_attr = option_tag['value']
        pjinfo_json = json.loads(value_attr)
        logger.info('提取pjinfo_json')
        #获取datatable
        datatable_data = f"xn={pjinfo_json['xn']}&xq={pjinfo_json['xq']}&pjlc={pjinfo_json['lcdm']}&sfzbpj={pjinfo_json['sfzbpj']}&sfwjpj={pjinfo_json['sfwjpj']}&pjzt_m={pjzt_m}&pjfsbz={pjinfo_json['pjfsdz']}&qyxjkc=&records=&menucode={menucode}&hidKey=&xwtj=1&menucode_current={menucode}"
        datatable_result = self.session.post(url=api.datatable_api,headers = self.headers,data=datatable_data)
        logger.info('获取datatable')
        #分割数据
        datatable_soup = BeautifulSoup(datatable_result.text, "html.parser")
        datatabletable = datatable_soup.find_all(attrs={'onclick': True})
        for tr in datatabletable:
            onclick_value = tr['onclick']
            start_index = len("parent.jxpj(\"")
            end_index = onclick_value.index("\",\"0\")")
            json_string = onclick_value[start_index:end_index]
            a = json_string.replace('\\"', '"')
            datatable_json = json.loads(a)
            #修改请求头
            Referer = f"https://jw.zzife.edu.cn/student/wspj_tjzbpj_wjdcb_pj.jsp?jsid={datatable_json['jsid']}&sfzjjs={datatable_json['sfzjjs']}&jsgh={datatable_json['gh']}&jsxm={datatable_json['xm']}&pjlb_m={datatable_json['pjlb_m']}&pjlbmc={datatable_json['pjlbmc']}&xn={datatable_json['xn']}&xq={datatable_json['xq']}&pjlc={datatable_json['pjlc']}&kcdm={datatable_json['kcdm']}&kcmc={datatable_json['kcmc']}&xf={datatable_json['xf']}&sfzbpj={pjinfo_json['sfzbpj']}&sfwjpj={pjinfo_json['sfwjpj']}&pjzt_m={pjzt_m}&pjfsbz=0&ypflag={datatable_json['ypflag']}&mode=0&yhdm={datatable_json['yhdm']}&skbjdm={datatable_json['skbjdm']}"
            Referer = urllib.parse.quote(Referer)
            self.headers['Referer'] = Referer
            #编写请求正文（教评数据）默认全为A
            jp_data = f"wspjZbpjWjdcForm.pjlb_m={datatable_json['pjlb_m']}&wspjZbpjWjdcForm.sfzjjs={datatable_json['sfzjjs']}&wspjZbpjWjdcForm.commitZB=60@0001@01;5@0002@01;5@0003@01;5@0004@01;5@0005@01;5@0006@01;5@0007@01;5@0008@01;5@0009@01;0@0010@01;0@0011@01;0@0012@01;&wspjZbpjWjdcForm.commitWJText=&wspjZbpjWjdcForm.commitWJSelect=&wspjZbpjWjdcForm.xn={datatable_json['xn']}&wspjZbpjWjdcForm.xq={datatable_json['xq']}&wspjZbpjWjdcForm.jsid={datatable_json['jsid']}&wspjZbpjWjdcForm.kcdm={datatable_json['kcdm']}&wspjZbpjWjdcForm.skbjdm={datatable_json['skbjdm']}&wspjZbpjWjdcForm.pjlc={datatable_json['pjlc']}&wspjZbpjWjdcForm.userCode={self.userid}&wspjZbpjWjdcForm.pjzt_m={pjzt_m}&wspjZbpjWjdcForm.zbmb_m=001&wspjZbpjWjdcForm.wjmb_m=&bfzfs_xx=&bfzfs_sx=&totalcj=100&zbSize=12&zbmb=001&cj0=60@0001@01&cj1=5@0002@01&cj2=5@0003@01&cj3=5@0004@01&cj4=5@0005@01&cj5=5@0006@01&cj6=5@0007@01&cj7=5@0008@01&cj8=5@0009@01&cj9=0@0010@01&cj10=0@0011@01&cj11=0@0012@01&menucode_current=S902"
            logger.info('编写教评请求正文')
            result = self.session.post(url=api.jpsave_api, headers=self.headers, data=jp_data)
            if result.text['message'] == "操作成功!":
                r = f"教评{datatable_json['xn']}"
                logger.info(r)
            else:
                logger.info(result.text['message'])
            time.sleep(3)
            return result.text['message']

    def run(self):
        try:
            logger.info("\n-----*任务开始执行*-----")
            result = self.login()
            print(result)
            logger.info(f"成功登陆{self.userid}")
            mode = str(input('请输入模式\n'
                             '获取课表：1\n'
                             '获取培养方案：2\n'
                             '获取成绩：3\n'
                             '教评：4\n'
                             '退出：5\n'))
            while mode != '5' and mode in ['1','2','3','4']:
                try:
                    if mode == '1':
                            result = self.timetable()
                    elif mode == '2':
                            result = self.cultivation_plan()
                    elif mode == '3':
                            result = self.get_grade()
                    elif mode == '4':
                            result = self.jp()
                    print(result)
                    logger.info(result)
                    if '未获取' in result:
                        logger.warning(result)
                except Exception as err:
                    print("\n-----*程序运行异常退出*-----")
                    logger.error("\n-----*程序运行异常退出*-----", exc_info=True)
                except KeyboardInterrupt:
                    # 手动中断程序运行
                    print("\n手动中断程序运行")
                    logger.info("\n手动中断程序运行")
                mode = str(input('请输入模式\n'
                             '获取课表：1\n'
                             '获取培养方案：2\n'
                             '获取成绩：3\n'
                             '教评：4\n'
                             '退出：5\n'))
        except Exception as err:
            print("\n-----*程序运行异常退出*-----")
            logger.error("\n-----*程序运行异常退出*-----", exc_info=True)
        except KeyboardInterrupt:
            # 手动中断程序运行
            print("\n手动中断程序运行")
            logger.info("\n手动中断程序运行")
        else:
            # 任务执行完毕
            logger.info("\n-----*任务执行完毕, 程序退出*-----")